#include "StdAfx.h"
#include "IppH264Decoder.h"
#include "umc_h264_dec_defs_dec.h"

using namespace UMC;



static Util::CritSec s_ippInitlock;
static bool          s_hasIppInit = false;

static void EnsureIppLibInit()
{
	Util::AutoLock lock(s_ippInitlock);

	if (!s_hasIppInit)
	{
		ippStaticInit();
		s_hasIppInit = true;
	}
}





IppH264Decoder::IppH264Decoder(void)
	:m_initFlag(false),
	m_decWidth(0),
	m_decHeight(0)
{
	EnsureIppLibInit();

	SplitterParams splParams;
	splParams.m_lFlags = VIDEO_SPLITTER;
	splParams.m_pDataReader = &m_frameReader;
	m_iDataSize = 0;
	m_h264Splitter.Init(splParams);
}

IppH264Decoder::~IppH264Decoder(void)
{
}

bool IppH264Decoder::InitDecoder()
{
	VideoProcessingParams postProcessingParams;
	VideoDecoderParams params;

	postProcessingParams.m_DeinterlacingMethod = NO_DEINTERLACING;
	postProcessingParams.InterpolationMethod = 0;
	m_videoProccessing.SetParams(&postProcessingParams);


	params.pPostProcessing  = &m_videoProccessing;
	//params.pPostProcessing  = &m_ptrCopy;
	params.info.stream_type = H264_VIDEO;
	params.numThreads = 1;//ippGetNumCoresOnDie();
	params.lFlags = 0;
	params.m_pData = &m_inputFrame;

	if (UMC_OK != m_decoder.Init(&params))
		return false;

	H264VideoDecoderParams h264Params;
	if (UMC_OK != m_decoder.GetInfo(&h264Params))
		return false;

	m_outputFrame.SetAlignment(1);
	m_outputFrame.Init(h264Params.info.clip_info.width, 
						h264Params.info.clip_info.height, 
						UMC::YV12, 8);
	m_outputFrame.Alloc();

	m_initFlag = true;

	return true;
}

int IppH264Decoder::DecodeH264Data(const uint8_t *data, uint32_t dataLen, uint8_t *yuvBuf, uint32_t yuvBufSize, bool &getFrame)
{
	Status nRet = UMC_OK; 
	getFrame = false;

	//check param
	if (dataLen>BUFFER_SIZE || !data)
		return -1;
	//analyze sps pps idr
	m_outputFrame.Reset();

	const uint8_t* p = data;
	bool bPPS = false;
	bool bIPB = false;
	int nNalType = 0;
	
	while(data+dataLen-p>4)
	{
	if ((p[0]!=0 || p[1]!=0 || p[2]!=0 || p[3]!=1))
	{
		p++;
		continue;
	}
	p+=4;
	nNalType = *p & 0X1F;
	switch (nNalType)
	{
		case NAL_UT_SPS:
			{
				while (data+dataLen-p>4)
				{
					if (p[0]==0 && p[1]==0&&p[2]==0&&p[3]==1&&(p[4]&0x1F)==NAL_UT_PPS)
					{
						m_inputFrame.SetBufferPointer((Ipp8u*)data,dataLen);
						m_inputFrame.SetDataSize(dataLen);
						bPPS = true;
						bIPB = true;
						m_iDataSize = 0;
						break;
					}
					else
					{
						p++;
					}
				}
				if (!bPPS)
				{
					memcpy(m_Buffer,data,dataLen);
					m_iDataSize = dataLen;
					return dataLen;
				}
				break;
			}
		case NAL_UT_PPS:
			{
				memcpy(m_Buffer+m_iDataSize,data,dataLen);
				m_iDataSize += dataLen;		
				return dataLen;
			}
		case NAL_UT_IDR_SLICE:
			{
				memcpy(m_Buffer+m_iDataSize,data,dataLen);
				m_iDataSize += dataLen;		

				m_inputFrame.SetBufferPointer((Ipp8u*)m_Buffer,m_iDataSize);
				m_inputFrame.SetDataSize(m_iDataSize);
				m_iDataSize = 0;
				bIPB = true;

				break;
			}
		case NAL_UT_SLICE:
		case NAL_UT_DPA:
		case NAL_UT_DPB:
		case NAL_UT_DPC:
			{
				m_inputFrame.SetBufferPointer((Ipp8u*)data,dataLen);
				m_inputFrame.SetDataSize(dataLen);
				bIPB = true;
				break;
			}

		default:
			{
				p++;
				break;
			}
	}
	
	if (bIPB)
		break;

	}
	//init source data 

	//m_inputFrame.SetBufferPointer((Ipp8u*)data,dataLen);
	//m_inputFrame.SetDataSize(dataLen);
	//init decoder only once
	if(!m_initFlag)
	{
		if (!InitDecoder())
		{
			return -1;
		}
	}
	
	//decode one frame to yuv
#if 0
	char tmp[50];
	static int i=1;
	sprintf(tmp,"D:\\video%d.yuv",i++);
	static FILE* fp = fopen("D:\\video.yuv","wb");
	char buff[100]={0};
#endif

	nRet = m_decoder.GetFrame(&m_inputFrame, &m_outputFrame);
	if (UMC_OK == nRet)
	{
		getFrame = true;
		m_decWidth = m_outputFrame.GetWidth();
		m_decHeight = m_outputFrame.GetHeight();

		if (yuvBufSize >= m_outputFrame.GetDataSize())
			memcpy(yuvBuf, m_outputFrame.GetDataPointer(), m_outputFrame.GetDataSize());

		return dataLen;
	}


	if (m_decoder.GetFrame(NULL, &m_outputFrame) == UMC_OK)
	{
		getFrame = true;
		m_decWidth = m_outputFrame.GetWidth();
		m_decHeight = m_outputFrame.GetHeight();
		yuvBufSize  = m_decWidth*m_decHeight*3/2;

		if (yuvBufSize >= m_outputFrame.GetDataSize())
			memcpy(yuvBuf, m_outputFrame.GetDataPointer(), m_outputFrame.GetDataSize());

#if 0
		fwrite(yuvBuf,1,yuvBufSize,fp);
		fflush(fp);
#endif
		return dataLen;
	}


	return nRet;

}

int IppH264Decoder::GetDecodedVideoSize(uint32_t &width, uint32_t &height) const
{
	if (!m_initFlag)
		return -2;

	width = m_decWidth;
	height = m_decHeight;

	return 0;
}